home *** CD-ROM | disk | FTP | other *** search
- /*
- * (c) Copyright 1992 by Panagiotis Tsirigotis
- * All rights reserved. The file named COPYRIGHT specifies the terms
- * and conditions for redistribution.
- */
-
- static char RCSid[] = "$Id: service.c,v 5.2 1992/11/10 08:18:25 panos Exp $" ;
-
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netdb.h>
- #include <syslog.h>
- #include <fcntl.h>
-
- #include "fsma.h"
- #include "sio.h"
-
- #include "options.h"
-
- #include "access.h"
- #include "attr.h"
- #include "service.h"
- #include "server.h"
- #include "state.h"
- #include "config.h"
- #include "connection.h"
- #include "logoptions.h"
-
- #define SUSPEND( sp ) (sp)->state = SVC_SUSPENDED
- #define RESUME( sp ) (sp)->state = SVC_ACTIVE
- #define DISABLE( sp ) (sp)->state = SVC_DISABLED
-
-
- void msg() ;
- void out_of_memory() ;
-
- time_t time() ;
-
- static fsma_h service_allocator ;
-
- static struct name_value service_states[] =
- {
- { "Not started", (int) SVC_NOT_STARTED },
- { "Active", (int) SVC_ACTIVE },
- { "Disabled", (int) SVC_DISABLED },
- { NULL, 0 }
- } ;
-
-
-
- status_e svc_init()
- {
- int flags = FSM_RETURN_ERROR + FSM_ZERO_ALLOC ;
-
- #ifdef DEBUG
- flags |= FSM_ZERO_FREE ;
- #endif
- service_allocator = fsm_create( sizeof( struct service ), 0, flags ) ;
- return( ( service_allocator == NULL ) ? FAILED : OK ) ;
- }
-
-
- /*
- * Allocate a new struct service and initialize it from scp
- */
- struct service *svc_new( scp )
- struct service_config *scp ;
- {
- struct service *sp ;
- char *func = "svc_new" ;
-
- sp = SP( fsm_alloc( service_allocator ) ) ;
- if ( sp == NULL )
- {
- out_of_memory( func ) ;
- return( NULL ) ;
- }
-
- if ( scp != NULL )
- *CONF( sp ) = *scp ;
- return( sp ) ;
- }
-
-
- /*
- * Allocate a new struct service, initialize it from scp and
- * insert it in stab
- */
- struct service *svc_alloc( scp, stab )
- struct service_config *scp ;
- pset_h stab ;
- {
- struct service *sp ;
- char *func = "svc_alloc" ;
-
- if ( ( sp = svc_new( scp ) ) == NULL )
- return( NULL ) ;
-
- if ( pset_add( stab, sp ) == NULL )
- {
- out_of_memory( func ) ;
- fsm_free( service_allocator, (char *) sp ) ;
- return( NULL ) ;
- }
- return( sp ) ;
- }
-
-
- /*
- * NOTE: We assume that the service_allocator has been initialized
- */
- void svc_free( sp )
- struct service *sp ;
- {
- sconf_free( CONF( sp ) ) ;
- fsm_free( service_allocator, (char *) sp ) ;
- }
-
-
- void svc_setup_address_control( sp )
- struct service *sp ;
- {
- register struct service_data *sdp = SDATA( sp ) ;
- register struct service_config *scp = CONF( sp ) ;
-
- if ( IS_PRESENT( scp, A_ONLY_FROM ) )
- sdp->only_from = scp->only_from ;
- else if ( SPECIFIED( DEFAULTS( ps ), A_ONLY_FROM ) )
- sdp->only_from = DEFAULTS( ps )->only_from ;
- else
- sdp->only_from = NULL ;
- if ( IS_PRESENT( scp, A_NO_ACCESS ) )
- sdp->no_access = scp->no_access ;
- else if ( SPECIFIED( DEFAULTS( ps ), A_NO_ACCESS ) )
- sdp->no_access = DEFAULTS( ps )->no_access ;
- else
- sdp->no_access = NULL ;
- }
-
-
- PRIVATE status_e set_fd_modes( sp )
- struct service *sp ;
- {
- struct service_config *scp = CONF( sp ) ;
- int sd = SVC_FD( sp ) ;
- char *func = "set_fd_modes" ;
-
- /*
- * There is a possibility of blocking on a send/write if
- *
- * the service is internal AND
- * it does not require forking another process AND
- * it does not accept connections
- *
- * To avoid this, we put the descriptor in FNDELAY mode.
- * (if the service accepts connections, we still need to put the
- * 'accepted' connection in FNDELAY mode but this is done in start_server)
- */
- if ( IS_INTERNAL( scp ) &&
- sp->builtin->fork_server == NO &&
- ! ACCEPTS_CONNECTIONS( scp ) &&
- fcntl( sd, F_SETFL, FNDELAY ) == -1 )
- {
- msg( LOG_ERR, func,
- "fcntl failed (%m) for FNDELAY. service = %s", scp->id ) ;
- return( FAILED ) ;
- }
-
- /*
- * Always set the close-on-exec flag
- */
- if ( fcntl( sd, F_SETFD, 1 ) == -1 )
- {
- msg( LOG_ERR, func,
- "fcntl failed (%m) for close-on-exec. service = %s", scp->id ) ;
- return( FAILED ) ;
- }
- return( OK ) ;
- }
-
-
- #ifndef NO_RPC
-
- PRIVATE status_e activate_rpc( sp )
- register struct service *sp ;
- {
- struct sockaddr_in sin ;
- int sin_len ;
- struct service_config *scp = CONF( sp ) ;
- struct rpc_data *rdp = RPCDATA( scp ) ;
- unsigned long vers ;
- unsigned registered_versions = 0 ;
- int sd = SVC_FD( sp ) ;
- char *func = "activate_rpc" ;
-
- CLEAR( sin ) ;
-
- sin.sin_family = AF_INET ;
- sin.sin_addr.s_addr = htonl( INADDR_ANY ) ;
- sin.sin_port = 0 ; /* let the system give us a port */
- if ( bind( sd, SA( &sin ), sizeof( sin ) ) == -1 )
- {
- msg( LOG_ERR, func, "bind failed (%m). service = %s", scp->id ) ;
- return( FAILED ) ;
- }
-
- /*
- * Find the port number that was assigned to the socket
- */
- sin_len = sizeof( sin ) ;
- if ( getsockname( sd, SA( &sin ), &sin_len ) == -1 )
- {
- msg( LOG_ERR, func, "getsockname failed (%m). service = %s", scp->id ) ;
- return( FAILED ) ;
- }
- scp->port = ntohs( sin.sin_port ) ;
-
- /*
- * Try to register as many versions as possible
- */
- for ( vers = rdp->min_version ; vers <= rdp->max_version ; vers++ )
- if ( pmap_set( rdp->program_number, vers,
- scp->protocol.value, scp->port ) )
- registered_versions++ ;
- else
- msg( LOG_ERR, func,
- "pmap_set failed. service=%s program=%ld version=%ld",
- scp->id, rdp->program_number, vers ) ;
-
- if ( debug.on )
- msg( LOG_DEBUG, func,
- "Registered %d versions of %s", registered_versions, scp->id ) ;
-
- return( ( registered_versions == 0 ) ? FAILED : OK ) ;
- }
-
- #endif /* ! NO_RPC */
-
-
- PRIVATE status_e activate_normal( sp )
- register struct service *sp ;
- {
- struct sockaddr_in sin ;
- int sd = SVC_FD( sp ) ;
- struct service_config *scp = CONF( sp ) ;
- char *func = "activate_normal" ;
-
- CLEAR( sin ) ;
-
- sin.sin_family = AF_INET ;
- sin.sin_addr.s_addr = htonl( INADDR_ANY ) ;
- sin.sin_port = htons( scp->port ) ;
-
- if ( reuse_option || M_IS_SET( scp->flags, SF_REUSE ) )
- {
- int on = 1 ;
-
- if ( setsockopt( sd, SOL_SOCKET,
- SO_REUSEADDR, (char *) &on, sizeof( on ) ) == -1 )
- msg( LOG_WARNING, func,
- "setsockopt SO_REUSEADDR failed (%m). service = %s", scp->id ) ;
- }
-
- if ( bind( sd, SA( &sin ), sizeof( sin ) ) == -1 )
- {
- msg( LOG_ERR, func, "bind failed (%m). service = %s", scp->id ) ;
- return( FAILED ) ;
- }
- return( OK ) ;
- }
-
-
- /*
- * Activate a service.
- */
- status_e svc_activate( sp )
- register struct service *sp ;
- {
- struct service_config *scp = CONF( sp ) ;
- struct service_data *sdp = SDATA( sp ) ;
- status_e status ;
- char *func = "activate_service" ;
- voidfunc get_shutdown_by_name() ;
- status_e start_log() ;
- status_e svc_generic_handler() ;
- void deactivate() ;
-
- sdp->service_fd = socket( AF_INET, scp->socket_type, scp->protocol.value ) ;
-
- if ( sdp->service_fd == -1 )
- {
- msg( LOG_ERR, func,
- "socket creation failed (%m). service = %s", scp->id ) ;
- return( FAILED ) ;
- }
-
- if ( set_fd_modes( sp ) == FAILED )
- {
- (void) close( sdp->service_fd ) ;
- return( FAILED ) ;
- }
-
- #ifndef NO_RPC
- if ( IS_RPC( scp ) )
- status = activate_rpc( sp ) ;
- else
- #endif /* ! NO_RPC */
- status = activate_normal( sp ) ;
-
- if ( status == FAILED )
- {
- (void) close( sdp->service_fd ) ;
- return( FAILED ) ;
- }
-
- if ( start_log( sp ) == FAILED )
- {
- deactivate( sp ) ;
- return( FAILED ) ;
- }
-
- /*
- * Initialize the service data
- */
- sdp->running_servers = sdp->retry_servers = 0 ;
- sdp->postmortem = server_postmortem ;
- sdp->shutdown = get_shutdown_by_name( scp->name ) ;
- sdp->handler = svc_generic_handler ;
- svc_setup_address_control( sp ) ;
-
- if ( MUST_LISTEN( scp ) )
- (void) listen( sdp->service_fd, LISTEN_BACKLOG ) ;
-
- ps.rws.descriptors_free-- ;
-
- sp->state = SVC_ACTIVE ;
-
- FD_SET( sdp->service_fd, &ps.rws.socket_mask ) ;
- if ( sdp->service_fd > ps.rws.mask_max )
- ps.rws.mask_max = sdp->service_fd ;
-
- ps.rws.active_services++ ;
- ps.rws.available_services++ ;
-
- return( OK ) ;
- }
-
-
- PRIVATE void deactivate( sp )
- register struct service *sp ;
- {
- (void) close( SVC_FD( sp ) ) ;
-
- #ifndef NO_RPC
- if ( IS_RPC( CONF( sp ) ) )
- {
- register unsigned long vers ;
- register struct rpc_data *rdp = RPCDATA( CONF( sp ) ) ;
-
- for ( vers = rdp->min_version ; vers <= rdp->max_version ; vers++ )
- (void) pmap_unset( rdp->program_number, vers ) ;
- }
- #endif /* ! NO_RPC */
- }
-
-
- /*
- * Close the service descriptor.
- * If an RPC service, deregister it.
- * Close the log.
- */
- void svc_deactivate( sp )
- register struct service *sp ;
- {
- void end_log() ;
-
- if ( ! IS_AVAILABLE( sp ) )
- return ;
-
- deactivate( sp ) ;
- ps.rws.descriptors_free++ ;
-
- if ( SDATA( sp )->log_handle )
- end_log( LOG( CONF( sp ) )->log_type, SDATA( sp )->log_handle ) ;
-
- if ( IS_ACTIVE( sp ) )
- {
- FD_CLR( SVC_FD( sp ), &ps.rws.socket_mask ) ;
- ps.rws.active_services-- ;
- }
-
- ps.rws.available_services-- ;
-
- DISABLE( sp ) ;
- }
-
-
-
- /*
- * Suspend a service
- */
- void svc_suspend( sp )
- struct service *sp ;
- {
- char *func = "suspend_service" ;
-
- if ( ! IS_ACTIVE( sp ) )
- {
- msg( LOG_ERR, func, "service %s is not active", CONF( sp )->id ) ;
- return ;
- }
-
- FD_CLR( SVC_FD( sp ), &ps.rws.socket_mask ) ;
- ps.rws.active_services-- ;
- if ( debug.on )
- msg( LOG_DEBUG, func, "Suspended service %s", CONF( sp )->id ) ;
-
- SUSPEND( sp ) ;
- }
-
-
- /*
- * Resume a suspended service.
- */
- void svc_resume( sp )
- struct service *sp ;
- {
- char *func = "resume_service" ;
-
- if ( IS_SUSPENDED( sp ) )
- {
- FD_SET( SVC_FD( sp ), &ps.rws.socket_mask ) ;
- ps.rws.active_services++ ;
- if ( debug.on )
- msg( LOG_DEBUG, func, "Resumed service %s", CONF( sp )->id ) ;
- RESUME( sp ) ;
- }
- }
-
-
- /*
- * Steps:
- * 1. Deactivate the service
- * 2. Free all memory used by the service and free the service itself
- *
- * Since this function may free all memory associated with the service as
- * well as the memory pointed by sp, only the value of sp should be used
- * after this call (i.e. no dereferencing of sp).
- */
- int svc_release( sp )
- struct service *sp ;
- {
- char *func = "svc_release" ;
- char *sid = CONF( sp )->id ;
-
- if ( sp->ref_count == 0 )
- {
- msg( LOG_ERR, func, "%s: svc_release with 0 count", sid ) ;
- return( 0 ) ;
- }
-
- sp->ref_count-- ;
- if ( sp->ref_count == 0 )
- {
- if ( debug.on )
- msg( LOG_DEBUG, func, "ref count of service %s dropped to 0", sid ) ;
- svc_deactivate( sp ) ;
- svc_free( sp ) ;
- return( 0 ) ;
- }
- else
- return( sp->ref_count ) ;
- }
-
-
-
- void svc_dump( sp, fd )
- struct service *sp ;
- int fd ;
- {
- register struct service_data *sdp = SDATA( sp ) ;
- struct name_value *nvp ;
- void sconf_dump() ;
- char *get_shutdown_by_addr() ;
- void tabprint() ;
-
- tabprint( fd, 0, "Service = %s\n", CONF( sp )->name ) ;
- tabprint( fd, 1, "State = %s\n",
- ( nvp = nv_find_name( service_states, (int) sp->state ) )
- ? nvp->name : "BAD STATE" ) ;
-
- sconf_dump( CONF( sp ), fd, 1, FALSE ) ;
-
- if ( sp->state == SVC_ACTIVE )
- {
- tabprint( fd, 1, "Service data\n" ) ;
- tabprint( fd, 2, "running servers = %d\n", sdp->running_servers ) ;
- tabprint( fd, 2, "retry servers = %d\n", sdp->retry_servers ) ;
- tabprint( fd, 2, "attempts = %d\n", sdp->attempts ) ;
- tabprint( fd, 2, "service fd = %d\n", sdp->service_fd ) ;
- tabprint( fd, 2, "post mortem function = %p\n", sdp->postmortem ) ;
- tabprint( fd, 2, "shutdown function = %s\n",
- get_shutdown_by_addr( sdp->shutdown ) ) ;
- }
- Sputchar( fd, '\n' ) ;
- }
-
-
- /*
- * Returns TRUE if the server instantiation rate is over the limit
- */
- bool_int svc_looping( sp )
- struct service *sp ;
- {
- register struct service_data *sdp = SDATA( sp ) ;
- time_t current_time ;
- time_t time_diff ;
- char *func = "loop_check" ;
-
- (void) time( ¤t_time ) ;
-
- if ( sdp->attempts == 0 )
- {
- sdp->attempts++ ;
- sdp->start_time = current_time ;
- return( FALSE ) ;
- }
-
- time_diff = current_time - sdp->start_time ;
- if ( time_diff <= LOOP_INTERVAL )
- {
- sdp->attempts++ ;
- if ( time_diff == 0 )
- time_diff = 1 ;
- if ( sdp->attempts/time_diff > ps.ros.loop_rate )
- {
- FD_CLR( SVC_FD( sp ), &ps.rws.socket_mask ) ;
- svc_deactivate( sp ) ;
- msg( LOG_ERR, func,
- "%s service was deactivated because of looping", CONF( sp )->id ) ;
- return( TRUE ) ;
- }
- }
- else
- {
- sdp->start_time = current_time ;
- sdp->attempts = 1 ;
- }
- return( FALSE ) ;
- }
-
-
-
- void svc_request( sp )
- struct service *sp ;
- {
- connection_s *cp ;
-
- cp = conn_new( sp ) ;
- if ( cp == NULL )
- return ;
-
- if ( SVC_HANDLE( sp, cp ) == OK )
- {
- if ( SVC_FORKS( sp ) )
- {
- conn_close( cp ) ;
- if ( CONF( sp )->wait == YES )
- svc_suspend( sp ) ;
- }
- else
- conn_free( cp ) ;
- }
- else
- {
- conn_cleanup( cp ) ;
- if ( conn_start_alternative( cp ) == FAILED )
- conn_free( cp ) ;
- }
- }
-
-
-
- PRIVATE status_e svc_generic_handler( sp, cp )
- struct service *sp ;
- connection_s *cp ;
- {
- struct service_config *scp = CONF( sp ) ;
-
- /*
- * There is no loop check for services that accept connections because
- * the connections are accepted by xinetd, therefore the queue will get
- * empty eventually even if the server is faulty.
- * Services for which no fork(2) occurs are not checked because
- * a) they are internal and we assume they are not faulty
- * b) we have no way of determining if something is going wrong
- * for example, UDP discard can do its job very quickly
- */
- if ( !ACCEPTS_CONNECTIONS( scp ) && SVC_FORKS( sp ) && svc_looping( sp ) )
- return( FAILED ) ;
-
- if ( svc_access_control( sp, cp ) == OK )
- return( server_run( sp, cp ) ) ;
-
- if ( M_IS_SET( scp->log_on_failure, LO_USERID ) &&
- ACCEPTS_CONNECTIONS( scp ) )
- (void) conn_add_alternative( cp, LOG_SERVICE( ps ) ) ;
-
- if ( SDATA( sp )->shutdown != NULL )
- {
- if ( ! M_IS_SET( scp->log_on_failure, LO_RECORD ) ||
- conn_add_alternative( cp,
- SHUTDOWN_SERVICE( ps ) ) == FAILED )
- conn_shutdown( cp ) ;
- }
-
- return( FAILED ) ;
- }
-
-
- status_e svc_access_control( sp, cp )
- struct service *sp ;
- connection_s *cp ;
- {
- access_e result ;
- access_e access_control() ;
- void log_failure() ;
-
- result = access_control( sp, cp, MASK_NULL ) ;
- if ( result != AC_OK )
- {
- log_failure( result, sp, cp ) ;
- return( FAILED ) ;
- }
- return( OK ) ;
- }
-
-
- /*
- * Implement shutdown protocol (usually sends an error indication).
- */
- void svc_shutdown( sp, cp )
- struct service *sp ;
- connection_s *cp ;
- {
- register struct service_config *scp = CONF( sp ) ;
- int old_flags ;
- int fd = cp->descriptor ;
- bool_int change_flags = ! ACCEPTS_CONNECTIONS( scp ) ;
- char *func = "svc_shutdown" ;
-
- if ( SDATA( sp )->shutdown == NULL )
- return ;
-
- /*
- * Ensure that we won't block in the shutdown function
- */
- if ( change_flags && ( old_flags = fcntl( fd, F_GETFL, 0 ) ) == -1 )
- {
- msg( LOG_ERR, func, "fcntl-getflags failed: %m" ) ;
- return ;
- }
-
- if ( fcntl( fd, F_SETFL, FNDELAY ) == -1 )
- msg( LOG_ERR, func, "fcntl-setflags failed: %m" ) ;
- else
- (*SDATA( sp )->shutdown)( fd, (char **)0 ) ;
-
- if ( change_flags )
- (void) fcntl( fd, F_SETFL, old_flags ) ;
- }
-
-